{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Tce3stUlHN0L"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "tuOe1ymfHZPu"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qFdPvlXBOdUN"
      },
      "source": [
        "# Keras 예제의 양자화 인식 훈련"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MfBg1C5NB3X0"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/model_optimization/guide/quantization/training_example\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/model_optimization/guide/quantization/training_example.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Run in Google Colab</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/model_optimization/guide/quantization/training_example.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub에서 소스 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/model_optimization/guide/quantization/training_example.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드하기</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Bjmi3qZeu_xk"
      },
      "source": [
        "## 개요\n",
        "\n",
        "*양자화 인식 훈련*의 엔드 투 엔드 예제를 시작합니다.\n",
        "\n",
        "### 기타 페이지\n",
        "\n",
        "양자화 인식 훈련이 무엇인지에 대한 소개와 이를 사용해야 하는지에 대한 결정(지원되는 내용 포함)은 [개요 페이지](https://www.tensorflow.org/model_optimization/guide/quantization/training.md)를 참조하세요.\n",
        "\n",
        "사용 사례에 필요한 API를 빠르게 찾으려면(8bit로 모델을 완전히 양자화하는 것 이상), [종합 가이드](https://www.tensorflow.org/model_optimization/guide/quantization/training_comprehensive_guide.md)를 참조하세요.\n",
        "\n",
        "### 요약\n",
        "\n",
        "이 튜토리얼에서는 다음을 수행합니다.\n",
        "\n",
        "1. MNIST용 `tf.keras` 모델을 처음부터 훈련합니다.\n",
        "2. 양자화 인식 학습 API를 적용하여 모델을 미세 조정하고, 정확성을 확인하고, 양자화 인식 모델을 내보냅니다.\n",
        "3. 모델을 사용하여 TFLite 백엔드에 대해 실제로 양자화된 모델을 만듭니다.\n",
        "4. TFLite와 4배 더 작아진 모델에서 정확성의 지속성을 확인합니다. 모바일에서의 지연 시간 이점을 확인하려면, [TFLite 앱 리포지토리](https://www.tensorflow.org/lite/models)에서 TFLite 예제를 사용해 보세요."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yEAZYXvZU_XG"
      },
      "source": [
        "## 설정"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zN4yVFK5-0Bf"
      },
      "outputs": [],
      "source": [
        "! pip uninstall -y tensorflow\n",
        "! pip install -q tf-nightly\n",
        "! pip install -q tensorflow-model-optimization\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yJwIonXEVJo6"
      },
      "outputs": [],
      "source": [
        "import tempfile\n",
        "import os\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "from tensorflow import keras"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "psViY5PRDurp"
      },
      "source": [
        "## 양자화 인식 훈련 없이 MNIST 모델 훈련하기"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pbY-KGMPvbW9"
      },
      "outputs": [],
      "source": [
        "# Load MNIST dataset\n",
        "mnist = keras.datasets.mnist\n",
        "(train_images, train_labels), (test_images, test_labels) = mnist.load_data()\n",
        "\n",
        "# Normalize the input image so that each pixel value is between 0 to 1.\n",
        "train_images = train_images / 255.0\n",
        "test_images = test_images / 255.0\n",
        "\n",
        "# Define the model architecture.\n",
        "model = keras.Sequential([\n",
        "  keras.layers.InputLayer(input_shape=(28, 28)),\n",
        "  keras.layers.Reshape(target_shape=(28, 28, 1)),\n",
        "  keras.layers.Conv2D(filters=12, kernel_size=(3, 3), activation='relu'),\n",
        "  keras.layers.MaxPooling2D(pool_size=(2, 2)),\n",
        "  keras.layers.Flatten(),\n",
        "  keras.layers.Dense(10)\n",
        "])\n",
        "\n",
        "# Train the digit classification model\n",
        "model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "model.fit(\n",
        "  train_images,\n",
        "  train_labels,\n",
        "  epochs=1,\n",
        "  validation_split=0.1,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "K8747K9OE72P"
      },
      "source": [
        "## 양자화 인식 훈련으로 사전 훈련된 모델 복제 및 미세 조정\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "F19k7ExXF_h2"
      },
      "source": [
        "### 모델 정의하기"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JsZROpNYMWQ0"
      },
      "source": [
        "전체 모델에 양자화 인식 훈련을 적용하고 모델 요약에서 이를 확인합니다. 이제 모든 레이어 앞에 \"quant\"가 붙습니다.\n",
        "\n",
        "결과 모델은 양자화를 인식하지만, 양자화되지는 않습니다(예: 가중치가 int8 대신 float32임). 다음 섹션에서는 양자화 인식 모델에서 양자화된 모델을 만드는 방법을 보여줍니다.\n",
        "\n",
        "[종합 가이드](https://www.tensorflow.org/model_optimization/guide/quantization/training_comprehensive_guide.md)에서 모델 정확성의 향상을 위해 일부 레이어를 양자화하는 방법을 볼 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oq6blGjgFDCW"
      },
      "outputs": [],
      "source": [
        "import tensorflow_model_optimization as tfmot\n",
        "\n",
        "quantize_model = tfmot.quantization.keras.quantize_model\n",
        "\n",
        "# q_aware stands for for quantization aware.\n",
        "q_aware_model = quantize_model(model)\n",
        "\n",
        "# `quantize_model` requires a recompile.\n",
        "q_aware_model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "q_aware_model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uDr2ijwpGCI-"
      },
      "source": [
        "### 기준선과 비교하여 모델 훈련 및 평가하기"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XUBEn94hXYB1"
      },
      "source": [
        "하나의 epoch 동안 모델을 훈련한 후 미세 조정을 시연하려면 훈련 데이터의 하위 집합에 대한 양자화 인식 훈련으로 미세 조정합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_PHDGJryE31X"
      },
      "outputs": [],
      "source": [
        "train_images_subset = train_images[0:1000] # out of 60000\n",
        "train_labels_subset = train_labels[0:1000]\n",
        "\n",
        "q_aware_model.fit(train_images_subset, train_labels_subset,\n",
        "                  batch_size=500, epochs=1, validation_split=0.1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-byC2lYlMkfN"
      },
      "source": [
        "이 예제의 경우, 기준선과 비교하여 양자화 인식 훈련 후 테스트 정확성의 손실이 거의 없습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6bMFTKSSHyyZ"
      },
      "outputs": [],
      "source": [
        "_, baseline_model_accuracy = model.evaluate(\n",
        "    test_images, test_labels, verbose=0)\n",
        "\n",
        "_, q_aware_model_accuracy = q_aware_model.evaluate(\n",
        "   test_images, test_labels, verbose=0)\n",
        "\n",
        "print('Baseline test accuracy:', baseline_model_accuracy)\n",
        "print('Quant test accuracy:', q_aware_model_accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2IepmUPSITn6"
      },
      "source": [
        "## TFLite 백엔드를 위한 양자화 모델 생성하기"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1FgNP4rbOLH8"
      },
      "source": [
        "다음을 통해 int8 가중치 및 uint8 활성화를 사용하여 실제로 양자화된 모델을 얻게 됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "w7fztWsAOHTz"
      },
      "outputs": [],
      "source": [
        "converter = tf.lite.TFLiteConverter.from_keras_model(q_aware_model)\n",
        "converter.optimizations = [tf.lite.Optimize.DEFAULT]\n",
        "\n",
        "quantized_tflite_model = converter.convert()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BEYsyYVqNgeY"
      },
      "source": [
        "## TF에서 TFLite까지 정확성의 지속성 확인하기"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "saadXD4JQsBK"
      },
      "source": [
        "테스트 데이터세트에 대해 TF Lite 모델을 평가하는 도우미 함수를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "b8yBouuGNqls"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "def evaluate_model(interpreter):\n",
        "  input_index = interpreter.get_input_details()[0][\"index\"]\n",
        "  output_index = interpreter.get_output_details()[0][\"index\"]\n",
        "\n",
        "  # Run predictions on every image in the \"test\" dataset.\n",
        "  prediction_digits = []\n",
        "  for i, test_image in enumerate(test_images):\n",
        "    if i % 1000 == 0:\n",
        "      print('Evaluated on {n} results so far.'.format(n=i))\n",
        "    # Pre-processing: add batch dimension and convert to float32 to match with\n",
        "    # the model's input data format.\n",
        "    test_image = np.expand_dims(test_image, axis=0).astype(np.float32)\n",
        "    interpreter.set_tensor(input_index, test_image)\n",
        "\n",
        "    # Run inference.\n",
        "    interpreter.invoke()\n",
        "\n",
        "    # Post-processing: remove batch dimension and find the digit with highest\n",
        "    # probability.\n",
        "    output = interpreter.tensor(output_index)\n",
        "    digit = np.argmax(output()[0])\n",
        "    prediction_digits.append(digit)\n",
        "\n",
        "  print('\\n')\n",
        "  # Compare prediction results with ground truth labels to calculate accuracy.\n",
        "  prediction_digits = np.array(prediction_digits)\n",
        "  accuracy = (prediction_digits == test_labels).mean()\n",
        "  return accuracy"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TuEFS4CIQvUw"
      },
      "source": [
        "양자화 모델을 평가하고 TensorFlow의 정확성이 TFLite 백엔드까지 유지되는지 확인합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VqQTyqz4NsWd"
      },
      "outputs": [],
      "source": [
        "interpreter = tf.lite.Interpreter(model_content=quantized_tflite_model)\n",
        "interpreter.allocate_tensors()\n",
        "\n",
        "test_accuracy = evaluate_model(interpreter)\n",
        "\n",
        "print('Quant TFLite test_accuracy:', test_accuracy)\n",
        "print('Quant TF test accuracy:', q_aware_model_accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "z8D7WnFF5DZR"
      },
      "source": [
        "## 양자화로 4배 더 작아진 모델 확인하기"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "I1c2IecBRCdQ"
      },
      "source": [
        "float TFLite 모델을 생성한 다음 TFLite 양자화 모델이 4배 더 작아진 것을 확인합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jy_Lgfh8VkyX"
      },
      "outputs": [],
      "source": [
        "# Create float TFLite model.\n",
        "float_converter = tf.lite.TFLiteConverter.from_keras_model(model)\n",
        "float_tflite_model = float_converter.convert()\n",
        "\n",
        "# Measure sizes of models.\n",
        "_, float_file = tempfile.mkstemp('.tflite')\n",
        "_, quant_file = tempfile.mkstemp('.tflite')\n",
        "\n",
        "with open(quant_file, 'wb') as f:\n",
        "  f.write(quantized_tflite_model)\n",
        "\n",
        "with open(float_file, 'wb') as f:\n",
        "  f.write(float_tflite_model)\n",
        "\n",
        "print(\"Float model in Mb:\", os.path.getsize(float_file) / float(2**20))\n",
        "print(\"Quantized model in Mb:\", os.path.getsize(quant_file) / float(2**20))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0O5xuci-SonI"
      },
      "source": [
        "## 결론"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "O2I7xmyMW5QY"
      },
      "source": [
        "이 튜토리얼에서는 TensorFlow Model Optimization Toolkit API를 사용하여 양자화 인식 모델을 만든 다음 TFLite 백엔드용 양자화 모델을 만드는 방법을 살펴보았습니다.\n",
        "\n",
        "정확성 차이를 최소화하면서 MNIST 모델의 크기를 4배 압축하는 이점을 확인했습니다. 모바일에서의 지연 시간 이점을 확인하려면, [TFLite 앱 리포지토리](https://www.tensorflow.org/lite/models)에서 TFLite 예제를 사용해 보세요.\n",
        "\n",
        "이 새로운 기능은 리소스가 제한된 환경에서 배포할 때 특히 중요하므로 사용해 볼 것을 권장합니다.\n"
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "training_example.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
